using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.ComponentModel;

namespace WbsEditor
{
    class WbsLayoutEngine
    {
        public static Size LayoutWbs(WorkBreakdownStructure wbs, WbsSettings settings, WbsTfsClient tfsClient)
        {
            WbsLayoutEngine engine = new WbsLayoutEngine(wbs, settings);
            Size size = engine.LayoutTree();
            engine.DoPlausibilityCheck(tfsClient);
            return size;
        }

        public static WbsNode FindNode(WbsNode node, Point point)
        {
            if (node.LayoutInfo.BoundingBox.Contains(point))
                return node;
            foreach (WbsNode childNode in node.ChildNodes)
            {
                WbsNode foundNode = FindNode(childNode, point);
                if (null != foundNode)
                    return foundNode;
            }
            return null;
        }

        public static bool CheckCollapseAction(WbsNode node, Point p)
        {
            if (node.HasChildNodes)
            {
                if (node.LayoutInfo.CollapseBox.Contains(p))
                {
                    node.IsCollapsed = !node.IsCollapsed;
                    return true;
                }
            }
            foreach (WbsNode child in node.ChildNodes)
            {
                bool ret = CheckCollapseAction(child, p);
                if (ret)
                    return ret;
            }

            return false;
        }

        public static bool IsVisible(WbsNode node)
        {
            return !HasCollapsedAncestor(node);
        }

        public static bool HasCollapsedAncestor(WbsNode node)
        {
            if (node.IsRoot)
                return false;
            if (null == node.Parent)
                return false;
            if (node.Parent.IsCollapsed)
                return true;
            return HasCollapsedAncestor(node.Parent);
        }

        public static Size LayoutDashboard(DashboardData data, WbsSettings settings, WbsTfsClient tfsClient)
        {
            WbsLayoutEngine engine = new WbsLayoutEngine(data, settings, tfsClient);
            Size size = engine.LayoutDashboard();
            return size;
        }

        public static WbsNode FindNode(DashboardData data, Point point)
        {
            foreach (IterationNodes iteration in data.IterationNodes)
            {
                foreach (WbsNode node in iteration.NodeList)
                {
                    if (node.LayoutInfo.BoundingBox.Contains(point))
                        return node;
                }
            }
            return null;
        }

        private WbsLayoutEngine(WorkBreakdownStructure wbs, WbsSettings settings)
        {
            _root = wbs.Root;
            _wbs = wbs;
            _settings = settings;
        }

        private WbsLayoutEngine(DashboardData data, WbsSettings settings, WbsTfsClient tfsClient)
        {
            _dashboardData = data;
            _settings = settings;
            _tfsClient = tfsClient;
        }

        private WorkBreakdownStructure _wbs;
        private WbsNode _root;
        private WbsSettings _settings;
        private DashboardData _dashboardData;
        private WbsTfsClient _tfsClient;

        private WbsSettings Settings { get { return _settings; } }

        //private enum LayoutMethod
        //{ 
        //    Horizontal = 0,
        //    Vertical = 1
        //}

        private Size LayoutTree()
        {
            ResetLayout(_root);
            LayoutSubTree(_root, "");
            return _root.LayoutInfo.SubTreeSize;
        }

        private Size LayoutDashboard()
        {
            ResetDashboard();

            int maxX = 0;
            int maxY = 0;

            for (int i = 0; i < _dashboardData.IterationNodes.Length; ++i)
            {
                IterationNodes nodes = _dashboardData.IterationNodes[i];
                nodes.MinX = i * (2 * Settings.HorizontalPadding + Settings.DefaultNodeSize.Width);
                nodes.MaxX = (i + 1) * (2 * Settings.HorizontalPadding + Settings.DefaultNodeSize.Width) - 1;
                for (int n = 0; n < nodes.NodeList.Count; ++n)
                {
                    WbsNode node = nodes.NodeList[n];
                    node.LayoutInfo.Position =
                        new Point(Settings.HorizontalPadding * (2*i + 1) + node.LayoutInfo.Size.Width * i,
                            Settings.VerticalPaddingAtRoot * 2 + n * (Settings.VerticalPadding + node.LayoutInfo.Size.Height));

                    int thisMaxX = node.LayoutInfo.BoundingBox.Right + Settings.HorizontalPadding;
                    int thisMaxY = node.LayoutInfo.BoundingBox.Bottom + Settings.VerticalPadding;
                    if (maxX < thisMaxX)
                        maxX = thisMaxX;
                    if (maxY < thisMaxY)
                        maxY = thisMaxY;
                }
                if (maxX < nodes.MaxX)
                    maxX = nodes.MaxX;
            }

            return new Size(maxX, maxY);
        }

        private void ResetDashboard()
        {
            foreach (IterationNodes iteration in _dashboardData.IterationNodes)
            {
                foreach (WbsNode node in iteration.NodeList)
                {
                    ResetNode(node);
                }
            }
        }

        private void ResetLayout(WbsNode node)
        {
            ResetNode(node);

            foreach (WbsNode childNode in node.ChildNodes)
            {
                ResetLayout(childNode);
            }
        }

        private void ResetNode(WbsNode node)
        {
            node.LayoutInfo.Position = new Point(0, 0);
            node.LayoutInfo.Size = Settings.DefaultNodeSize;
            node.LayoutInfo.SubTreeSize = Settings.DefaultNodeSize;
        }

        //private void LayoutSubTree(WbsNode node, string pspCode)
        //{
        //    if (node.IsRoot)
        //    {
        //        LayoutSubTree(node, LayoutMethod.Horizontal, pspCode);
        //    }
        //    else
        //    {
        //        LayoutSubTree(node, LayoutMethod.Vertical, pspCode);
        //    }
        //}

        private void LayoutSubTree(WbsNode node, /*LayoutMethod method, */string pspCode)
        {
            if (Settings.UseWbsCodes)
                node.PspCode = pspCode;
            else
                node.PspCode = "";
            if (node.ChildNodes.Count == 0)
            {
                return;
            }

            if (!string.IsNullOrEmpty(pspCode))
                pspCode += ".";
            for (int i=0; i<node.ChildNodes.Count; ++i)
            {
                LayoutSubTree(node.ChildNodes[i], pspCode + (i+1));
            }

            //if (LayoutMethod.Horizontal == method)
            if (node.LayoutMode == WbsNode.NodeLayout.Horizontal)
            {
                int yOffset = node.LayoutInfo.Size.Height;
                if (node.IsRoot)
                    yOffset += Settings.VerticalPaddingAtRoot;
                else
                    yOffset += Settings.VerticalPadding;
                int xOffset = 0;
                int maxY = 0;

                if (!node.IsCollapsed)
                {
                    foreach (WbsNode childNode in node.ChildNodes)
                    {
                        OffsetTree(childNode, xOffset, yOffset);
                        xOffset += childNode.LayoutInfo.SubTreeSize.Width + Settings.HorizontalPadding;
                        int thisY = childNode.LayoutInfo.SubTreeSize.Height + yOffset;
                        if (thisY > maxY)
                            maxY = thisY;
                    }
                }
                else
                {
                    xOffset = node.LayoutInfo.Size.Width + Settings.HorizontalPadding;
                }
                int maxX = xOffset - Settings.HorizontalPadding;

                // center root
                int rootXOffset = (maxX - node.LayoutInfo.Size.Width) / 2;
                node.LayoutInfo.Position = new Point(rootXOffset, node.LayoutInfo.Position.Y);
                // Note bounding box size
                node.LayoutInfo.SubTreeSize = new Size(maxX, maxY);
            }
            else if (node.LayoutMode == WbsNode.NodeLayout.Paired)
            {
                int maxX = node.LayoutInfo.Size.Width;
                int maxY = node.LayoutInfo.Size.Height;
                List<int> levelOffset = new List<int>();
                // Left side
                {
                    int yOffset = node.LayoutInfo.Size.Height + Settings.VerticalPadding;

                    if (!node.IsCollapsed)
                    {
                        int level = 0;
                        for (int i = 0; i < node.ChildNodes.Count; ++i)
                        {
                            if ((i % 2) != 0)
                                continue;
                            WbsNode child = node.ChildNodes[i];
                            int xOffset = 0;
                            OffsetTree(child, xOffset, yOffset);
                            yOffset += child.LayoutInfo.SubTreeSize.Height + Settings.VerticalPadding;
                            levelOffset.Add(yOffset);
                            level++;
                            int thisY = yOffset - Settings.VerticalPadding;
                            if (thisY > maxY)
                                maxY = thisY;
                            int thisX = child.LayoutInfo.SubTreeSize.Width;
                            if (thisX > maxX)
                                maxX = thisX;
                        }
                    }
                }

                int leftMaxX = maxX;
                // Right side
                {
                    int xOffset = leftMaxX + 2 * Settings.HorizontalPadding;
                    //int xOffset = Settings.IndentationVerticalLayout;
                    //int yOffset = node.LayoutInfo.Size.Height + Settings.VerticalPadding;
                    int yOffset = node.LayoutInfo.Size.Height + Settings.VerticalPadding;

                    if (!node.IsCollapsed)
                    {
                        int level = 0;
                        for (int i = 0; i < node.ChildNodes.Count; ++i)
                        {
                            if ((i % 2) == 0)
                                continue;
                            WbsNode child = node.ChildNodes[i];
                            //int xOffset = xMid + Settings.IndentationVerticalLayout;
                            OffsetTree(child, xOffset, yOffset);
                            yOffset += child.LayoutInfo.SubTreeSize.Height + Settings.VerticalPadding;
                            //if (levelOffset[level] > yOffset)
                            //    yOffset = levelOffset[level];
                            level++;
                            int thisY = yOffset - Settings.VerticalPadding;
                            if (thisY > maxY)
                                maxY = thisY;
                            int thisX = child.LayoutInfo.SubTreeSize.Width + xOffset;
                            if (thisX > maxX)
                                maxX = thisX;
                        }
                    }
                }

                if (!node.IsCollapsed)
                {
                    // Center root
                    int rootXOffset = leftMaxX + Settings.HorizontalPadding - node.LayoutInfo.Size.Width / 2;
                    node.LayoutInfo.Position = new Point(rootXOffset, node.LayoutInfo.Position.Y);
                }
                node.LayoutInfo.SubTreeSize = new Size(maxX, maxY);
            }
            else
            {
                int xOffset = Settings.IndentationVerticalLayout;
                int yOffset = node.LayoutInfo.Size.Height + Settings.VerticalPadding;

                int maxX = node.LayoutInfo.Size.Width;
                if (!node.IsCollapsed)
                {
                    foreach (WbsNode childNode in node.ChildNodes)
                    {
                        OffsetTree(childNode, xOffset, yOffset);
                        yOffset += childNode.LayoutInfo.SubTreeSize.Height + Settings.VerticalPadding;
                        int thisX = childNode.LayoutInfo.SubTreeSize.Width + xOffset;
                        if (thisX > maxX)
                            maxX = thisX;
                    }
                }
                int maxY = yOffset - Settings.VerticalPadding;
                node.LayoutInfo.SubTreeSize = new Size(maxX, maxY);
            }
        }

        private void OffsetTree(WbsNode node, int xOffset, int yOffset)
        {
            int x = node.LayoutInfo.Position.X;
            int y = node.LayoutInfo.Position.Y;

            node.LayoutInfo.Position = new Point(x + xOffset, y + yOffset);

            foreach (WbsNode childNode in node.ChildNodes)
            {
                OffsetTree(childNode, xOffset, yOffset);
            }
        }

        private void DoPlausibilityCheck(WbsTfsClient tfsClient)
        {
            WbsPlausiChecker.CheckNodeRecursively(_wbs, _root, tfsClient);
        }
    }
}
